package util

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"gitee.com/sheldwu/httpdns/config"

	"github.com/spf13/viper"
)

//doveclient config*********************
var DoveClientAddr = "/var/lib/doveclient/doveclient.sock"

var DialDoveClientTimeout = time.Second * 3

var ReadDoveClientTimeout = time.Second * 3

var WriteDoveClientTimeout = time.Second * 3

var dovekeyNotExist = errors.New("key is not exist")

var doveConn net.Conn

var RwDovelock *sync.RWMutex
var RwBfirstLock *sync.Mutex

var bFirstInit = true

func init() {
	RwDovelock = new(sync.RWMutex)
	RwBfirstLock = new(sync.Mutex)

}

// 初始化配置
func Init() {
	// 初始化需要
	if IsFirstInit() {
		ch := make(chan bool)
		go AsyncDove(ch)
		<-ch
	}
}

func IsFirstInit() bool {
	RwBfirstLock.Lock()
	isFirst := bFirstInit
	bFirstInit = false
	defer func() {
		bFirstInit = false
		RwBfirstLock.Unlock()
	}()
	return isFirst
}

func AsyncDove(ch chan bool) {
	firstRound := true
	for {
		GetDoveConn()
		GetProvince2Idc()
		GetIdc2Mqtt()
		GetIdcDomainMap()
		getHttpDnsList()
		getRetryInternal()
		getProviderCodeMap()
		// tell worker channel on initial
		if firstRound {
			ch <- true
		}
		firstRound = false
		time.Sleep(3 * time.Second)
	}
}

func GetDoveConn() {
	viper.SetConfigFile("/etc/httpdns/config.toml")
	err := viper.ReadInConfig()
	if err != nil {
		log.Fatal(err)
	}

	doveAddr := viper.GetString("dove.addr")

	doveConn, err = net.DialTimeout("unix", doveAddr, time.Second*3)

	if err != nil {
		log.Fatal(err)
	}
}

func getDoveValue(doveConn net.Conn, doveKey string) ([]byte, error) {
	reqData := make(map[string]interface{})
	reqData["cmd"] = "GetProjectConfig"
	reqData["args"] = map[string]interface{}{"name": doveKey}

	bStr, _ := json.Marshal(reqData)

	headTag := []byte(strconv.Itoa(len(bStr)))
	headTag = append(headTag, make([]byte, 8-len(headTag))...)
	headTag = append(headTag, bStr...)

	doveConn.SetWriteDeadline(time.Now().Add(WriteDoveClientTimeout))

	n, err := doveConn.Write(headTag)
	if err != nil || n != len(headTag) {
		return nil, fmt.Errorf("write dove error. %s, %d, %d", err, n, len(headTag))
	}

	//从doveclient 读取返回
	doveConn.SetReadDeadline(time.Now().Add(ReadDoveClientTimeout))

	resHeadTag := make([]byte, 16)
	n, err = io.ReadFull(doveConn, resHeadTag)
	if err != nil || n != 16 {
		return nil, fmt.Errorf("read dove error. %s, %d, %d", err, n, 16)
	}

	//如果key值存在，会有一个ok的标志位
	if strings.Trim(string(resHeadTag[8:16]), "\000") != "ok" {
		return nil, dovekeyNotExist
	}

	bodyLen, err := strconv.Atoi(strings.Trim(string(resHeadTag[0:7]), "\000"))
	if err != nil {
		return nil, fmt.Errorf("read dove body length error. %s", err)
	}

	rspData := make([]byte, bodyLen)
	n, err = io.ReadFull(doveConn, rspData)
	if err != nil || n != bodyLen {
		return nil, fmt.Errorf("read dove error. %s, %d, %d", err, n, bodyLen)
	}

	return rspData, nil
}

func GetProvince2Idc() error {
	value, err := getDoveValue(doveConn, "HttpDns.Province2IDC")
	if err != nil {
		return err
	}
	defer RwDovelock.Unlock()
	RwDovelock.Lock()
	json.Unmarshal(value, &config.Province2Idc)

	return nil
}

func GetIdc2Mqtt() error {
	value, err := getDoveValue(doveConn, "HttpDns.IDC2Mqtt")
	if err != nil {
		return err
	}
	defer RwDovelock.Unlock()
	RwDovelock.Lock()
	json.Unmarshal(value, &config.Idc2Mqtt)

	return nil
}

func GetIdcDomainMap() error {
	value, err := getDoveValue(doveConn, "HttpDns.IDCDomainMap")
	if err != nil {
		return err
	}
	defer RwDovelock.Unlock()
	RwDovelock.Lock()
	json.Unmarshal(value, &config.Idc2DomainMap)

	return nil
}

func getHttpDnsList() error {
	value, err := getDoveValue(doveConn, "HttpDns.HttpDnsList")
	if err != nil {
		return err
	}
	defer RwDovelock.Unlock()
	RwDovelock.Lock()
	json.Unmarshal(value, &config.HttpDnsList)

	return nil
}

func getRetryInternal() error {
	value, err := getDoveValue(doveConn, "HttpDns.RetryInternal")
	if err != nil {
		return err
	}
	defer RwDovelock.Unlock()
	RwDovelock.Lock()
	json.Unmarshal(value, &config.RetryInternal)

	return nil
}

func getProviderCodeMap() error {
	value, err := getDoveValue(doveConn, "HttpDns.ProviderProvinceCode")
	if err != nil {
		return err
	}
	defer RwDovelock.Unlock()
	RwDovelock.Lock()
	json.Unmarshal(value, &config.ProviderProvinceCode)

	return nil
}
